home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************************************\
-
- printf module
-
- part of suntar, ©1991,1992 Sauro & Gabriele Speranza
-
- This program is public domain, feel free to use it or part of it for anything
-
- \*******************************************************************************/
-
- /* it's a not 100% standard version of printf: it does not support field
- length (%5d is NOT allowed) and has a new descriptor %P for Pascal strings
-
- However, it has a sophisticated handling of buffering and flushing
- */
-
- #define WIN_INDEX 0
- /* I found no interest in supporting windows other then window 0, but
- if you place the buffer and some flags in the window struct it would be
- easy to adapt it to write to any window; the easiest way is to use a variable
- as WIN_INDEX, obviously flushing the buffer every time it's changed
- */
-
- #define FILE_PRINT
- #include "windows.h"
- #include "suntar.h"
-
- static Boolean first_time=true;
- static char no_flush = 0;
- static char last_char=CR;
- short list_buf_size;
-
- void my_flush(Boolean);
- void printdec(long);
- void print_oct(long);
- void printhex(long,Boolean);
- void printf(char*,int);
- void new_console(void);
-
- static void new_console()
- {
- /* create the window: since no older window exists, new_window will
- use the index 0 (guessing the index is not good programming style, but
- since suntar uses only one window it works) */
- GrafPtr savePort;
- GetPort( &savePort );
- last_char=CR;
- new_window();
- first_time=false;
- SetPort( &my_windows[WIN_INDEX] );
- TEAutoView (true,my_windows[WIN_INDEX].TEH);
- SetPort( savePort );
- }
-
-
- void disable_autoflush(mode)
- short mode; /* 1: does not flush at the end of a printf, but only when the
- buffer is almost full (and the last character is a newline)
- 2: as above, and it does not scroll the window as
- part of the flush */
- {
- /* the printf routine updates the screen, adjusts the scroll bars and
- scrolls the window so that the cursor is in sight, but
- a) that takes a lot of time
- b) if the printed lines are larger than the window, that may yield a lot of
- silly horizontal scrolling
- Hence, in the most critical sections of suntar updates are delayed and
- scrolls are disabled.
- */
- if(first_time) /* i.e., if no console window still exists... */
- new_console();
- no_flush=mode;
- }
-
- void enable_autoflush()
- {
- no_flush=0;
- flush_console();
- }
-
- void flush_console()
- {
- GrafPtr savePort;
-
- if(!my_windows[WIN_INDEX].used) return;
- GetPort( &savePort );
- SetPort( &my_windows[WIN_INDEX] );
- my_flush(true);
-
- if( last_char==CR||last_char==LF){ /* to avoid horizontal scrolling, don't scroll
- to show the current cursor position if it's not at beginning of line */
- update_console();
- TESelView( my_windows[WIN_INDEX].TEH);
- }
- if(curr_window==&my_windows[WIN_INDEX]){
- SCR_BAR_AND_TEXT_IN_SYNC
- }
- /* else
- per ora la scrollbar è comunque bianca, non importa aggiornarla, poi ci pensa
- MainEvent al momento dell'evento di attivazione
- -- the scrollbar is currently white, it will be updated by MainEvent at the
- activate event */
-
- SetPort( savePort );
- }
-
-
- #ifndef SUNTAR
- /* suntar does not use it, preferring the more powerful prompt(), but it was
- tested and debugged, so you may use it if you need it: it's a full implementation
- of the standard gets function, allowing all Mac-style editing tools on the
- line which is being typed */
- char * gets(buf)
- char*buf;
- {
- short io_start;
-
- /* The window was read-only: now, it's broken in two parts, the bottom
- becomes read/write, but any existing text, up to the prompt which
- probably you printf-ed just before calling gets, remains read-only;
- furthermore, the read-write portion is one line (even Paste is altered
- so that you can't paste a newline character) */
- flush_all();
- io_start=my_windows[WIN_INDEX].lastPrompt=(**my_windows[WIN_INDEX].TEH).selEnd;
-
- /* wait until the user types a return or enter */
- do{
- MainEvent();
- }
- while(my_windows[WIN_INDEX].lastPrompt!=32767);
- /* set the window as read-only again, and copy what appears after the prompt
- in the output string */
- {register char *p= buf;
- register char *q= *((**my_windows[WIN_INDEX].TEH).hText) +io_start;
- register short i=(**my_windows[WIN_INDEX].TEH).teLength-io_start;
- while(i--)
- *p++=*q++;
- *--p='\0'; /* il '\n' non va ritornato... */
- return buf;
- }
- }
- #endif
-
- void start_of_line()
- {
- /* guarantees that the last character in the window is a carriage return */
- if(last_char!=CR&&last_char!=LF)
- put_char(CR);
- }
-
- void one_empty_line()
- /* guarantees that the last two characters in the window are carriage returns */
- {
- if(last_char!=CR){
- put_char(CR);
- if(last_char!=CR) /* could be LF now... */
- put_char(CR);
- }
- }
-
-
- void prompt(buf,nbytes)
- /* come gets, ma ha un controllo di overflow e un valore iniziale
- -- similar to gets, but it has a buffer size parameter and the
- text input line has an initial value, which is selected so that you
- may delete it by typing any character
- */
- char*buf;
- short nbytes;
- {
- short io_start;
- GrafPtr savePort;
-
- io_start=(**my_windows[WIN_INDEX].TEH).selEnd;
- if(buf[0]){
- #if 1 /* now Think C is more rigorous on type checks, and there is no way to match
- the parameters ( '...' in the prototype is NOT compatible with the use of
- vararg ! I have not tried with stdarg ).
- In such cases, the normal solution would be to compile this function with
- less type checking, but in Think C such flags are global, they can't be
- set/reset on part of the project */
- register char*p=buf;
- while(*p) put_char(*p++);
- put_char(' '); /* the space seems superfluous, but without it the whole
- line appears selected, and that's not very pretty to be seen */
- #else
- printf("%s ",buf);
- #endif
- flush_all();
- }
- GetPort( &savePort );
- SetPort(&my_windows[WIN_INDEX]);
- TESetSelect((long)io_start,(**my_windows[WIN_INDEX].TEH).selEnd-(buf[0]!=0),my_windows[WIN_INDEX].TEH);
- update_console();
- TESelView( my_windows[WIN_INDEX].TEH);
- SetPort(savePort);
- if(curr_window != &my_windows[WIN_INDEX]) /* potrebbe esserci un desk accessory */
- SelectWindow(&my_windows[WIN_INDEX]);
-
- my_windows[WIN_INDEX].lastPrompt=io_start;
-
- do{
- MainEvent();
- if(is_abort_command()){
- GetPort( &savePort );
- SetPort(&my_windows[WIN_INDEX]);
- TESetSelect((long)io_start,(long)32767,my_windows[WIN_INDEX].TEH);
- TEDelete(TEH);
- my_windows[WIN_INDEX].lastPrompt=32767; /* rimettilo readonly */
- SetPort(savePort);
- accept_abort_command();
- }
- }
- while(my_windows[WIN_INDEX].lastPrompt!=32767);
-
- {register char *p= buf;
- register char *q= *((**my_windows[WIN_INDEX].TEH).hText) +io_start;
- register short i=(**my_windows[WIN_INDEX].TEH).teLength-io_start;
- if(i>nbytes) i=nbytes;
- while(i--)
- *p++=*q++;
- *--p='\0'; /* il '\n' non va ritornato... */
- }
- }
-
- #include <varargs.h>
-
- void printf(fmt,va_alist)
- char*fmt;
- va_dcl
- {
- va_list ap;
- register char*format=fmt;
- long val;
- register char*string;
- short longform;
-
-
- if(first_time) new_console();
-
- va_start(ap);
- while(*format!='\0'){
- if(*format!='%')
- put_char(*format);
- else{
- format++;
- if(longform = *format=='l')
- format++;
- switch(*format){
- case '%':
- put_char('%');
- break;
- case 'D':
- longform=1; /* e prosegui...*/
- case 'd':
- if(!longform)
- val= (long) va_arg(ap,short);
- else
- val= va_arg(ap,long);
- printdec(val);
- break;
- case 'u':
- val= (long) va_arg(ap,unsigned short); /* I've never used %lu... */
- printdec(val);
- break;
- case 'X':
- longform=1; /* e prosegui...*/
- case 'x':
- if(!longform)
- val= (long) va_arg(ap,short);
- else
- val= va_arg(ap,long);
- printhex(val,longform);
- break;
- case 'o':
- if(!longform)
- val= (long) va_arg(ap,short);
- else
- val= va_arg(ap,long);
- print_oct(val);
- break;
- case 'c':
- put_char( va_arg(ap,short) );
- break;
- case 's':
- string= va_arg(ap,char*);
- while(*string)
- put_char(*string++);
- break;
- case 'P':
- /* è un formato non standard C, ma mi fa molto comodo: una stringa formato Pascal
- -- a non-standard C format: a Pascal string, extremely useful on the Mac
- */
- string= va_arg(ap,char*);
- val= *string++;
- while(--val>=0)
- put_char(*string++);
- break;
- default:
- SysBeep(5); /* invalid format descriptor */
- }
- }
- format++;
- }
- va_end(ap);
- if(!no_flush)
- flush_console();
- }
-
- static void printdec(val)
- register long val;
- {
- char buf[14];
- register short i=-1;
- if(val<0){
- val= -val;
- put_char('-');
- }
- do{
- buf[++i]='0'+(short)(val%10);
- val /= 10;
- }
- while(val>0);
- while(i>=0) put_char(buf[i--]);
-
- }
-
- static void print_oct(val)
- register long val;
- {
- char buf[14];
- register short i=-1;
- do{
- buf[++i]='0'+(short)(val&7);
- val >>=3;
- }
- while(val>0);
- while(i>=0) put_char(buf[i--]);
- }
-
- static void printhex(val,longform)
- register long val;
- Boolean longform;
- {
- short digit,i=longform?28:12;
- short non_in_testa=0;
- while(i>=0){
- if(!i) non_in_testa=1;
- digit= (val>>i) & 0xF;
- if(digit!=0 || non_in_testa){
- put_char (digit<=9 ? '0'+digit : 'A'-10+digit );
- non_in_testa=1;
- }
- i-=4;
- }
- }
-
-
- char *printf_buf;
- static short chars_in_buffer=0;
-
- void put_char(c)
- char c;
- {
- if(c==LF) c=CR;
- if(c!= CR || last_char==LF || last_char==CR )
- last_char=c;
- else
- last_char=LF; /* meaning one carriage return but not two, CR means
- at least two carriage returns */
- printf_buf[chars_in_buffer++]=c;
- if(chars_in_buffer>=list_buf_size || chars_in_buffer>=list_buf_size-64 && c==CR ){
- GrafPtr savePort;
- GetPort( &savePort );
- SetPort( &my_windows[WIN_INDEX]);
- my_flush(no_flush<2);
- SetPort( savePort );
- }
- }
-
- void update_console()
- {
- /* per uno stupido bug: il TextEdit non è pensato per scrivere su una finestra
- che sta in background e lo scroll (almeno quello automatico) non provvede a scrollare
- anche l'update region, col che l'update successivo è sulla posizione vecchia,
- non scrollata, del buco bianco. Unica soluzione, fare un update subito... ovviamente,
- per essere sicuri bisognerebbe chiamarla in continuazione, io mi limito a chiamarla
- quando chiudo una finestra di dialogo, quando ci sono forti probabilità di
- provocare un update
- -- TextEdit has a bad bug: the TESelView routine does scroll the text,
- but the update region is not scrolled (that is, OffsetRgn is not called) to
- remember that the "white hole" left by an old window now closed was moved.
- Hence, when the update event arrives it updates part of the window which needn't
- an update and leaves part of the white hole.
- Only solution by now: don't call TeSelView when the update region is not empty:
- that is, either don't use TextEdit to implement the printf function (as a text
- editor it's still good, unless you type so quickly that a KeyDown event arrives
- between the window closing and the update) or do your own implementation of TESelView
- or call this function just before calling TESelView
- */
- if(!my_windows[WIN_INDEX].used)
- return;
- else{
- RgnHandle h=((WindowPeek)&my_windows[WIN_INDEX])->updateRgn;
- RgnPtr p;
- if(h && (p= *h) && p->rgnSize>=10)
- if( *((long*)&p->rgnBBox) != *((long*)&p->rgnBBox.bottom) ) /* this test
- is redundant, but it may avoid a relatively expensive toolbox call */
- if(!EmptyRgn (h)) UpdateWindow(&my_windows[WIN_INDEX]);
- }
- }
-
-
- static void my_flush(do_scroll)
- Boolean do_scroll;
- {
- if(chars_in_buffer){
- /* window 0 is the console window, and old text must be deleted if the
- TextEdit limit of 32 kbytes is approached... */
- if( ((**my_windows[0].TEH).teLength >maxTElength-4000 ||
- /* according to TN 237, there is another limit to the size of a TextEdit
- record: the destination rect must not be taller than 32767 pixels:
- that limit is more stringent only if the line height in pixels is bigger
- than the average line length in characters, a very rare situation,
- unless you set the font size to 36 or 48, or use a small autowrap window */
- (**my_windows[0].TEH).nLines > 32000/(**my_windows[0].TEH).lineHeight) )
- SilentSuppression(50);
-
- TEAutoView (false,my_windows[WIN_INDEX].TEH); /* otherwise, a lot of horizontal scrolling...*/
- TESetSelect((long)32767,(long)32767,my_windows[WIN_INDEX].TEH);
- TEInsert(printf_buf,(long)chars_in_buffer,my_windows[WIN_INDEX].TEH);
- TEAutoView (true,my_windows[WIN_INDEX].TEH);
- if(do_scroll && (last_char==CR||last_char==LF) ){
- update_console();
- TESelView( my_windows[WIN_INDEX].TEH);
- }
- chars_in_buffer=0;
- }
- }